package net.woggle.shackbrowse;

import java.io.IOException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ListIterator;

import org.apache.http.client.ClientProtocolException;
import org.json.JSONException;
import org.json.JSONObject;

import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

import net.woggle.CheckableLinearLayout;
import net.woggle.CustomLinkMovementMethod;
import net.woggle.shackbrowse.TagConverter.ColoredUnderlineSpan;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.support.v4.app.ListFragment;
import android.support.v4.util.LruCache;
import android.text.ClipboardManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.ParagraphStyle;
import android.text.style.RelativeSizeSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.TypedValue;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

public class ThreadViewFragment extends SherlockListFragment
{
    public PostLoadingAdapter _adapter;
    int _rootPostId = 0;
    int _currentPostId = 0;
    int _selectPostIdAfterLoading = 0;
    String _currentPostAuthor;
    int _lastExpanded = 0;
    final static int FRAGMENT_ID = 20;
    final static int MENU_COPY_URL = 0;
    final static int MENU_COPY_TEXT = 1;
    
    private int _userNameHeight = 0;
    
    boolean _touchedFavoritesButton = false;
    
    ProgressDialog _progressDialog;
    
    // list view saved state while rotating
    private Parcelable _listState = null;
    private int _listPosition = 0;
    private int _itemPosition = 0;
    private int _itemChecked = ListView.INVALID_POSITION;
    
    JSONObject _lastThreadJson;
    
    private boolean _refreshRestore = false;
	private boolean _viewAvailable;
	
	private int _postYLoc = 0;
	boolean _autoFaveOnLoad = false;
	int _messageId = 0;
	String _messageSubject;
	private AsyncTask<String, Void, Void> _curTask;
    
    public int getPostId()
    {
        return _rootPostId;
    }
    
    @Override
    public void onCreate(Bundle savedInstanceBundle)
    {
    	
    	super.onCreate(savedInstanceBundle);
    	this.setRetainInstance(true);
    }

	@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {   
    	_viewAvailable = true;
        return inflater.inflate(R.layout.thread_view, null);
    }
    
    @Override
    public void onDestroyView()
    {
    	_viewAvailable = false;
    	super.onDestroyView();
    }
    
    public void loadPost(Post post)
    {        
		_adapter.add(post);
		
		// needs to be displaypost(position) not (post)
		expandAndCheckPost(_adapter.getPosition(post));
    }
    
    public void trackR(String label)
    {
    	((MainActivity)getActivity()).track("ui_action", "fakeActionBarClick", label);
    }
    
    
	@Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        super.onActivityCreated(savedInstanceState);

        // set list view up
        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        getListView().setDividerHeight(0);
        //getListView().setBackgroundColor(getResources().getColor(R.color.collapsed_postbg));

        if (_adapter == null)
        {
        	// first launch, try to set everything up
        	Bundle args = getArguments();
        	String action = getActivity().getIntent().getAction();
        	Uri uri = getActivity().getIntent().getData();
        	
        	// instantiate adapter
        	_adapter = new PostLoadingAdapter(getActivity(), new ArrayList<Post>());

        	setListAdapter(_adapter);
        	
        	//  only load this junk if the arguments isn't null
        	if (args != null)
        	{
        		if (args.containsKey("rootPostId"))
            	{
            		_rootPostId = args.getInt("rootPostId");
            		_currentPostId = _rootPostId;
            		_itemPosition = 0;
            	}
        		if (args.containsKey("messageId"))
            	{
            		_messageId = args.getInt("messageId");
            		_messageSubject = args.getString("messageSubject");
            	}
        		if (args.containsKey("autoFaveOnLoad"))
            	{
            		_autoFaveOnLoad  = args.getBoolean("autoFaveOnLoad");
            	}
        		if (args.containsKey("selectPostIdAfterLoading"))
        		{
    	    		_selectPostIdAfterLoading = args.getInt("selectPostIdAfterLoading");
        		}
            	if (args.containsKey("content"))
            	{
            		String userName = args.getString("userName");
            		String postContent = args.getString("content");
            		Long posted = args.getLong("posted");
            		String moderation = args.containsKey("moderation") ? args.getString("moderation") : "";
            		String lastThreadJson = args.containsKey("json") ? args.getString("json") : "";
            		try {
						_lastThreadJson = new JSONObject(lastThreadJson);
					} catch (JSONException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
            		
            		// create root post fast with no load delay
            		Post post = null;
            		if (_messageId > 0)
            			post = new Post(_messageId, userName, postContent, posted, 0, moderation, true);
            		else 
            			post = new Post(_rootPostId, userName, postContent, posted, 0, moderation, true);
            		
            		loadPost(post);
            	}
            	else if (action != null && action.equals(Intent.ACTION_VIEW) && uri != null)
            	{
            		String id = uri.getQueryParameter("id");
            		if (id == null)
            		{
            			ErrorDialog.display(getActivity(), "Error", "Invalid URL Found");
            			return;
            		}
                
            		_rootPostId = Integer.parseInt(id);
            		_currentPostId = _rootPostId;
            		_itemPosition = 0;
            	}
        	}
        	_adapter.triggerLoadMore();
        }
        else    
        {
       		// user rotated the screen, try to go back to where they where
       		if (_listState != null)
       			getListView().onRestoreInstanceState(_listState);
       		
       		getListView().setSelectionFromTop(_listPosition,  _itemPosition);
       		if (_itemChecked != ListView.INVALID_POSITION)
       			expandAndCheckPost(_itemChecked);
        }
        
        ((View)getListView().getParent()).findViewById(R.id.tview_refresh).setOnClickListener(new OnClickListener(){
    		
			@Override
			public void onClick(View v) {
				refreshThreadReplies();
				trackR("refreshReplies");
			}
    		
    	});
    	
    	((View)getListView().getParent()).findViewById(R.id.tview_fullscreen).setOnClickListener(new OnClickListener(){
    		
			@Override
			public void onClick(View v) {
				toggleFullScreen();
				trackR("toggleFullScreen");
			}
    		
    	});
    	
    	((View)getListView().getParent()).findViewById(R.id.tview_unfullscreen).setOnClickListener(new OnClickListener(){
    		
			@Override
			public void onClick(View v) {
				toggleFullScreen();
			}
    		
    	});
    	
    	((View)getListView().getParent()).findViewById(R.id.tview_preferences).setOnClickListener(new OnClickListener(){
    		
			@Override
			public void onClick(View v) {
				((MainActivity)getActivity()).toggleMenu();
				trackR("openOverflowMenuTView");
			}
    		
    	});
       	
    	
       	updateThreadViewUi();
    }
    
    public void updateThreadViewUi()
    {
    	if (_viewAvailable)
    	{
    		if (getListView() != null)
    		{
    			// handle the throbbers
    			if (_adapter != null)
	    		{
		    		if (_adapter.getCount() == 1)
		    		{
		    			// single post already loaded throbber
		    			_adapter.notifyDataSetChanged();
		    		}

	        		// throbber behind refresh button on dualpane view
	        		((View)getListView().getParent()).findViewById(R.id.tview_progressbar).setVisibility((_adapter.isCurrentlyLoading()) ? View.VISIBLE : View.GONE);
	        		((View)getListView().getParent()).findViewById(R.id.tview_progressbar_inactive).setVisibility((!_adapter.isCurrentlyLoading()) ? View.VISIBLE : View.GONE);
	            	
	    		}
    			
	    		// button bar setups for different modes
	    		if (_messageId != 0)
	    		{
	    			// messages mode
	    			((View)getListView().getParent()).findViewById(R.id.tview_buttonBarContainer).setVisibility(View.VISIBLE);
		       		((View)getListView().getParent()).findViewById(R.id.tview_buttonBarLine).setVisibility(View.VISIBLE);
		       		((View)getListView().getParent()).findViewById(R.id.tview_messagereply).setVisibility(View.VISIBLE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_refresh).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_startMsg).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_FSIcon).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_favsaved).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_favunsaved).setVisibility(View.GONE);
	    		}
	    		else if (_messageId == 0)
		       	{
	    			// normal thread mode mode
	    			((View)getListView().getParent()).findViewById(R.id.tview_messagereply).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_buttonBarContainer).setVisibility(View.VISIBLE);
		       		((View)getListView().getParent()).findViewById(R.id.tview_buttonBarLine).setVisibility(View.VISIBLE);
		       		// show the icon and start message if no threads or messages have been loaded
		    		((View)getListView().getParent()).findViewById(R.id.tview_FSIcon).setVisibility((_rootPostId > 0) ? View.GONE : View.VISIBLE);
		    		((View)getListView().getParent()).findViewById(R.id.tview_startMsg).setVisibility((_rootPostId > 0) ? View.GONE : View.VISIBLE);
		        	
		    		// if not throbbing, and refresh button not hidden by mode or startup nothread scenario, show refresh button
		    		((View)getListView().getParent()).findViewById(R.id.tview_refresh).setVisibility(
		        			(_rootPostId > 0)
		        			? View.VISIBLE : View.GONE);
		        	
		        	
		        	
		            // and provided a way to save thread
		            if ((_lastThreadJson != null) && (_adapter != null) && (_adapter.getCount() > 0) && (_adapter.getItem(0) != null))
		    		{
		    			// determine if checked
		        		boolean set2 = ((MainActivity)getActivity()).mOffline.containsThreadId(ThreadViewFragment.this._rootPostId);
		        		
		        		((View)getListView().getParent()).findViewById(R.id.tview_favsaved).setVisibility((set2) ? View.VISIBLE : View.GONE);
		            	((View)getListView().getParent()).findViewById(R.id.tview_favunsaved).setVisibility((set2) ? View.GONE : View.VISIBLE);
		
		            	((View)getListView().getParent()).findViewById(R.id.tview_favsaved).setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { saveThread(); dualPaneUpdFavButton(); trackR("favSaveReplyView"); } });
		            	((View)getListView().getParent()).findViewById(R.id.tview_favunsaved).setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { saveThread(); dualPaneUpdFavButton(); trackR("favUNSaveReplyView"); } });
		
		    		}
		            else
		            {
		            	((View)getListView().getParent()).findViewById(R.id.tview_favsaved).setVisibility((_rootPostId > 0) ? View.INVISIBLE : View.GONE);
		            	((View)getListView().getParent()).findViewById(R.id.tview_favunsaved).setVisibility(View.GONE);
		            }
		       	}
	    		
	    		if (((MainActivity)getActivity()).getDualPane())
	    		{
	    			boolean whatDo = ((MainActivity)getActivity()).getThreadViewFullScreen();
	        		((View)getListView().getParent()).findViewById(R.id.tview_unfullscreen).setVisibility((!whatDo) ? View.GONE : View.VISIBLE);
	        		((View)getListView().getParent()).findViewById(R.id.tview_fullscreen).setVisibility((whatDo) ? View.GONE : View.VISIBLE);

	    			((View)getListView().getParent()).findViewById(R.id.tview_preferences).setVisibility(View.GONE);
	    		}
	    		else
	    		{
	    			((View)getListView().getParent()).findViewById(R.id.tview_preferences).setVisibility(View.VISIBLE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_fullscreen).setVisibility(View.GONE);
	    			((View)getListView().getParent()).findViewById(R.id.tview_unfullscreen).setVisibility(View.GONE);
	    		}
	    		
	    		// handle the fullscreen throbber
	    		RelativeLayout FSLoad = (RelativeLayout) ((View)getListView().getParent()).findViewById(R.id.tview_FSLoad);
	    		if ((_adapter != null) && (_adapter.isCurrentlyLoading()) && (_adapter.getCount() == 0))
	    		{
	    			getListView().setVisibility(View.GONE);
	    			FSLoad.setVisibility(View.VISIBLE);
	    		}
	    		else
	    		{
	    			if ((_rootPostId > 0) || (_messageId > 0))
	    			{
	    				getListView().setVisibility(View.VISIBLE);
	    			}
	    			FSLoad.setVisibility(View.GONE);
	    		}
    		}
    	}
    }
    
	public void updateMessageReplyButton() {
		if (_viewAvailable)
		{
			ImageButton reply = (ImageButton)((View)getListView().getParent()).findViewById(R.id.tview_messagereply);
			reply.setOnClickListener(new View.OnClickListener() {
	    	    @Override
	    	    public void onClick(View v) {
	    	    	postReply(_adapter.getItem(0));
	    	    }
	    	});
		}
	}
    
    public void dualPaneUpdFavButton()
    {
		// determine if checked
		boolean set2 = ((MainActivity)getActivity()).mOffline.containsThreadId(ThreadViewFragment.this._rootPostId);
		
		((View)getListView().getParent()).findViewById(R.id.tview_favsaved).setVisibility((set2) ? View.VISIBLE : View.GONE);
    	((View)getListView().getParent()).findViewById(R.id.tview_favunsaved).setVisibility((set2) ? View.GONE : View.VISIBLE);
    }
    
    public void toggleFullScreen()
    {
    	if (_viewAvailable)
    	{
    		boolean whatDo = ((MainActivity)getActivity()).getThreadViewFullScreen();
    		((MainActivity)getActivity()).setThreadViewFullScreen(!whatDo);
    		((View)getListView().getParent()).findViewById(R.id.tview_unfullscreen).setVisibility((whatDo) ? View.GONE : View.VISIBLE);
    		((View)getListView().getParent()).findViewById(R.id.tview_fullscreen).setVisibility((!whatDo) ? View.GONE : View.VISIBLE);

    	}
    }
    
    private void searchForPosts(String term)
    {
    	Bundle args = new Bundle();
    	args.putString("author", term);
    	((MainActivity)getActivity()).openSearch(args);
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState)
    {
    	super.onSaveInstanceState(outState);

    	// we should put this info into the outState, but the compatibility framework
    	// seems to swallow it somewhere   	
    	saveListState();    			
    }
    
    public void saveListState()
    {
    	if (_viewAvailable)
    	{
	    	ListView listView = getListView();
	    	_listState = listView.onSaveInstanceState();
	    	_listPosition = listView.getFirstVisiblePosition();
	    	_itemChecked = _lastExpanded;
	    	View itemView = listView.getChildAt(0);
	    	_itemPosition = itemView == null ? 0 : itemView.getTop(); 
    	}
    }
    public void ensurePostSelectedAndDisplayed ()
    {
    	ensurePostSelectedAndDisplayed(_selectPostIdAfterLoading);
    }
    public void ensurePostSelectedAndDisplayed(int postId)
    {
        int length = _adapter.getCount();
        for (int i = 0; i < length; i++)
        {
            Post post = _adapter.getItem(i);
            if (post != null && post.getPostId() == postId)
            {
                expandAndCheckPost(i);
                
            	// dont select root posts. unnecessary
            	// i is position
        	
                
                if (_refreshRestore)
                {
                	getListView().setSelectionFromTop(_listPosition,_itemPosition);
                	_refreshRestore = false;
                }
                else
                	getListView().setSelection(i);
        	
                break;
            }
        }
    }
    
    public boolean isPostIdInAdapter(int postId)
    {
    	if (_adapter != null)
    	{
	        int length = _adapter.getCount();
	        for (int i = 0; i < length; i++)
	        {
	            Post post = _adapter.getItem(i);
	            if (post != null && post.getPostId() == postId)
	            {
	                return true;
	            }
	        }
    	}
		return false;
    }
    
    public void showTaggers(int pos)
    {
        if (_adapter.getItem(pos) != null && _adapter.getItem(pos).getPostId() > 0)
    	{
        	final GetTaggersTask gtt = new GetTaggersTask();
        	
        	gtt.execute(_adapter.getItem(pos).getPostId());
        }
    }
    class GetTaggersTask extends AsyncTask<Integer, Void, CharSequence> {
    	protected ProgressDialog mProgressDialog;
		private CharSequence arraylistFormatter (String type, ArrayList<String>  arr)
    	{
    		if (arr.size() > 0)
    		{
    			int color = 0;
    			if (type.equals("lol")) color = getResources().getColor(R.color.shacktag_lol);
    			if (type.equals("wtf")) color = getResources().getColor(R.color.shacktag_wtf);
    			if (type.equals("inf")) color = getResources().getColor(R.color.shacktag_inf);
    			if (type.equals("tag")) color = getResources().getColor(R.color.shacktag_tag);
    			if (type.equals("ugh")) color = getResources().getColor(R.color.shacktag_ugh);
    			if (type.equals("unf")) color = getResources().getColor(R.color.shacktag_unf);
    			SpannableString header = new SpannableString(type + "\'d" + "\n");
    			header.setSpan(new ForegroundColorSpan(color), 0, header.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    			header.setSpan(new RelativeSizeSpan(1.6f), 0, header.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    			
    			java.util.Collections.sort(arr, Collator.getInstance());
	    		ListIterator<String> iter = arr.listIterator();
	    		String txt = "";
	    		while (iter.hasNext())
	    			txt = txt + iter.next() + "\n";
	    		
	    		SpannableString list = new SpannableString(txt);
    			list.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.userName)), 0, txt.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
    			
				return TextUtils.concat(header, list, "\n");
    		}
    		else
    			return "";
    	}
        @Override
        protected CharSequence doInBackground(Integer... params) {
        	
        	final Integer parm = params[0];
        	getActivity().runOnUiThread(new Runnable(){
        		@Override public void run()
        		{
        			mProgressDialog = ProgressDialog.show(getActivity(), "Loading Taggers", "ThomW's server is a loose cannon", true, true, new OnCancelListener(){
    				@Override
    				public void onCancel(DialogInterface arg0) {
    					cancel(true);
    					System.out.println("CANCEED");
    				}});
        		}
			});
        	
        	ArrayList<String> resultslol = new ArrayList<String>();
        	ArrayList<String> resultsinf = new ArrayList<String>();
        	ArrayList<String> resultstag = new ArrayList<String>();
        	ArrayList<String> resultsunf = new ArrayList<String>();
        	ArrayList<String> resultswtf = new ArrayList<String>();
        	ArrayList<String> resultsugh = new ArrayList<String>();
        	
        	try {
				resultslol = ShackApi.getLOLTaggers(parm, "lol");
				resultsinf = ShackApi.getLOLTaggers(parm, "inf");
				resultsunf = ShackApi.getLOLTaggers(parm, "unf");
				resultstag = ShackApi.getLOLTaggers(parm, "tag");
				resultswtf = ShackApi.getLOLTaggers(parm, "wtf");
				resultsugh = ShackApi.getLOLTaggers(parm, "ugh");
			} catch (Exception e) {
				e.printStackTrace();
			}
        	
        	CharSequence txt = TextUtils.concat(
        			arraylistFormatter("lol", resultslol),
        			arraylistFormatter("inf", resultsinf),
        			arraylistFormatter("unf", resultsunf),
        			arraylistFormatter("tag", resultstag),
        			arraylistFormatter("wtf", resultswtf),
        			arraylistFormatter("ugh", resultsugh)
        			);
            return txt;
        }
        @Override
        public void onPostExecute(final CharSequence txt)
        {
        	if (getActivity () != null)
    		{
    			getActivity().runOnUiThread(new Runnable(){
	        		@Override public void run()
	        		{
	        			AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
	                    builder.setTitle("Taggers for post");
	                    builder.setNegativeButton("OK", null);
	                    
				        ScrollView scrolly = new ScrollView(getActivity());
				        TextView content = new TextView(getActivity());
				        content.setPadding(10, 10, 10, 10);
				        content.setTextSize(TypedValue.COMPLEX_UNIT_SP, _adapter._zoom * getResources().getDimension(R.dimen.viewPostTextSize));
				        scrolly.addView(content);
				        content.setText(txt);
				        if (Build.VERSION.SDK_INT >= 11)
				        {
				        	content.setTextIsSelectable(true);
				        }
				        System.out.println("SETTING LOLLERS: " + txt);
				        builder.setView(scrolly);
				        AlertDialog alert = builder.create();
				        alert.show();
				        
				        mProgressDialog.dismiss();
	        		}
    			});
    		}
        }
    }
    
    public void shareURL(int pos)
    {
	    Intent sendIntent = new Intent();
	    sendIntent.setAction(Intent.ACTION_SEND);
	    sendIntent.putExtra(Intent.EXTRA_TEXT, createPostURL(pos));
	    sendIntent.setType("text/plain");
	    startActivity(Intent.createChooser(sendIntent, "Share Post Link"));
    }
    private String createPostURL(int pos)
    {
    	if (_adapter.getItem(pos) != null && _adapter.getItem(pos).getPostId() > 0)
    	{
    		String str = "http://www.shacknews.com/chatty?id=" + _adapter.getItem(pos).getPostId();
    		if ((_lastExpanded > 0) && (pos > 0))
    		{
    			str = "http://www.shacknews.com/chatty?id=" + _adapter.getItem(pos).getPostId();
    			str = str + "#item_" + _adapter.getItem(pos).getPostId();
    		}
    		return str;
    	}
    	return null;
    }
    public void copyURL(int pos)
    {
    	if (createPostURL(pos) != null)
    	{
    		copyString(createPostURL(pos));
    	}
    }
    public void copyPostText(int pos)
    {
    	 copyString(_adapter.getItem(pos).getCopyText());
    }
    
    public void copyString(String string)
    {
    	ClipboardManager clipboard = (ClipboardManager)getActivity().getSystemService(Activity.CLIPBOARD_SERVICE);
    	clipboard.setText(string);
    	Toast.makeText(getActivity(), string, Toast.LENGTH_SHORT).show();
    }
    
    public void refreshThreadReplies()
    {
    	 saveListState();
    	 if (_adapter != null && _adapter.getCount() > _lastExpanded  && _adapter.getItem(_lastExpanded) != null)
    		 _selectPostIdAfterLoading = _adapter.getItem(_lastExpanded).getPostId();
    	 _refreshRestore = true;
     	
         _adapter.clear();
         _adapter.triggerLoadMore();
    }
    
    public void saveThread()
    {
    	if ((_lastThreadJson != null) && (_adapter != null) && (_adapter.getCount() > 0) && (_adapter.getItem(0) != null))
    	{
    		// false if UNsaved
    		if(((MainActivity)getActivity()).mOffline.toggleThread(_adapter.getItem(0).getPostId(), _adapter.getItem(0).getPosted(), _lastThreadJson))
    		{
    			Toast.makeText(getActivity(), "Thread added to favorites", Toast.LENGTH_SHORT).show();
    		}
    		else
    		{
    			Toast.makeText(getActivity(), "Thread removed from favorites", Toast.LENGTH_SHORT).show();
    		}
    		
    		((MainActivity)getActivity()).mRefreshOfflineThreadsWoReplies();
    	}
    	else
    	{
    		Toast.makeText(getActivity(), "Error: could not save thread", Toast.LENGTH_SHORT).show();
    		System.out.println("TVIEW: no json to save thread with");
    	}
    }
    
    public static final int POST_REPLY = 937;
    public static final int POST_MESSAGE = 947;
    private void postReply(final Post parentPost)
    {
        SharedPreferences _prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
        
        boolean verified = _prefs.getBoolean("usernameVerified", false);
        if (!verified)
        {
        	LoginForm login = new LoginForm(getActivity());
        	login.setOnVerifiedListener(new LoginForm.OnVerifiedListener() {
				@Override
				public void onSuccess() {
					postReply(parentPost);
				}

				@Override
				public void onFailure() {
				}
			});
        	return;
        }
        else if (_messageId == 0)
        {
	    	boolean isNewsItem = _adapter.getItem(0).getUserName().equalsIgnoreCase("shacknews");
		    ((MainActivity)getActivity()).openComposerForReply(POST_REPLY, parentPost, isNewsItem);
        }
        else if (_rootPostId == 0)
        {
        	((MainActivity)getActivity()).openComposerForMessageReply(POST_MESSAGE, parentPost, "Re: " + _messageSubject);
        }
        else
        {
        	 ErrorDialog.display(getActivity(), "Error", "Error determining message TYPE for reply.");
        }
    }
    
    public void usernameActions(final String username)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Choose Action");
        final CharSequence[] items = { "Shackmessage " + username, "Search for posts by " + username};
        builder.setItems(items, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
            	if (item == 1)
            	{
            		searchForPosts(username);
            	}
            	else if (item == 0)
            	{
            		shackmessageTo(username);
            	}
                }});
        AlertDialog alert = builder.create();
        alert.setCanceledOnTouchOutside(true);
        alert.show();
    }
    
    public void shackmessageTo (String username)
    {
    	((MainActivity)getActivity()).openNewMessagePromptForSubject(username);
    }
    
    public void lolChoose(int pos, final boolean isFromQuickLOL)
    {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ThreadViewFragment.this.getActivity());

        final int finpos = pos;
        
        boolean verified = prefs.getBoolean("usernameVerified", false);
        if (!verified)
        {
        	LoginForm login = new LoginForm(getActivity());
        	login.setOnVerifiedListener(new LoginForm.OnVerifiedListener() {
				@Override
				public void onSuccess() {
					lolChoose(finpos, isFromQuickLOL);
				}

				@Override
				public void onFailure() {
				}
			});
        	return;
        }
        
    	AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Choose LOL tag");
        final CharSequence[] items = { "lol","inf","unf","tag","wtf","ugh"};
        final CharSequence[] itemsQuick = { "See who tagged", "lol","inf","unf","tag","wtf","ugh"};
        if (isFromQuickLOL)
        {
        builder.setItems(itemsQuick, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
            	if (item == 0)
            	{
            		new GetTaggersTask().execute(_adapter.getItem(finpos).getPostId());
            	}
            	else
            		lolPost((String)itemsQuick[item], finpos);
                }});
        }
        else
        {
        builder.setItems(items, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                lolPost((String)items[item], finpos);
                }});
        }
        AlertDialog alert = builder.create();
        alert.setCanceledOnTouchOutside(true);
        alert.show();
    }
    
    private void lolPost(final String tag, final int pos)
    {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ThreadViewFragment.this.getActivity());
        String userName = prefs.getString("userName", "");
        
        boolean verified = prefs.getBoolean("usernameVerified", false);
        if (!verified)
        {
        	LoginForm login = new LoginForm(getActivity());
        	login.setOnVerifiedListener(new LoginForm.OnVerifiedListener() {
				@Override
				public void onSuccess() {
					lolPost(tag, pos);
				}

				@Override
				public void onFailure() {
				}
			});
        	return;
        }
        
        _curTask = new LolTask().execute(userName, tag, Integer.toString(pos));
        _progressDialog = ProgressDialog.show(getActivity(), "Please wait", "Tagging post as " + tag, true, true, new OnCancelListener(){
			@Override
			public void onCancel(DialogInterface arg0) {
				if (_curTask != null)
					_curTask.cancel(true);
			}});
        
        
    }
    
    public void modChoose()
    {
    	AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Choose mod tag");
        final CharSequence[] items = { "interesting","nws","stupid","tangent","ontopic","political" };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                modPost((String)items[item]);
                }});
        AlertDialog alert = builder.create();
        alert.setCanceledOnTouchOutside(true);
        alert.show();
    }
    
    private void modPost(final String moderation)
    {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ThreadViewFragment.this.getActivity());
        String userName = prefs.getString("userName", "");
        String password = prefs.getString("password", "");
        
        boolean verified = prefs.getBoolean("usernameVerified", false);
        if (!verified)
        {
        	LoginForm login = new LoginForm(getActivity());
        	login.setOnVerifiedListener(new LoginForm.OnVerifiedListener() {
				@Override
				public void onSuccess() {
					modPost(moderation);
				}

				@Override
				public void onFailure() {
				}
			});
        	return;
        }
        
        new ModTask().execute(userName, password, moderation);
        _progressDialog = ProgressDialog.show(getActivity(), "Please wait", "Laying down the ban hammer...");
    }
    
    class LolTask extends AsyncTask<String, Void, Void>
    {
        Exception _exception;
        
        @Override
        protected Void doInBackground(String... params)
        {
            String userName = params[0];
            String tag = params[1];
            int pos = Integer.parseInt(params[2]);
            
            
            try
            {
                ShackApi.tagPost(_adapter.getItem(pos).getPostId(), tag, userName);
            }
            catch (Exception ex)
            {
                Log.e("shackbrowse", "Error tagging post", ex);
                _exception = ex;
            }
            
            return null;
        }
        
        @Override
        protected void onPostExecute(Void result)
        {
            _progressDialog.dismiss();
            if (_exception != null)
               ErrorDialog.display(getActivity(), "Error", "Error tagging post:\n" + _exception.getMessage()); 
        }
    }
    
    class ModTask extends AsyncTask<String, Void, String>
    {
        Exception _exception;
        
        @Override
        protected String doInBackground(String... params)
        {
            String userName = params[0];
            String password = params[1];
            String moderation = params[2];
            
            try
            {
                int rootPost = _adapter.getItem(0).getPostId();
                String result = ShackApi.modPost(userName, password, rootPost, _currentPostId, moderation);
                return result;
            }
            catch (Exception e)
            {
                _exception = e;
                Log.e("shackbrowse", "Error modding post", e);
            }
            
            return null;
        }
        
        @Override
        protected void onPostExecute(String result)
        {
            _progressDialog.dismiss();
            if (_exception != null)
                ErrorDialog.display(getActivity(), "Error", "Error occured modding post."); 
            else if (result != null)
                ErrorDialog.display(getActivity(), "Moderation", result);
        }
        
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        switch (requestCode)
        {
        	// returning from compose post view
            case POST_REPLY:
                if (resultCode == Activity.RESULT_OK)
                {
                    // read the resulting thread id from the post
                	// this is either the id of your new post or the id of the post your replied to
                    int postId = data.getExtras().getInt("postId");
                    _selectPostIdAfterLoading = postId;
                    _currentPostId = postId;
                    _adapter.clear();
                    _adapter.triggerLoadMore();
                }
                break;
            default:
                break;
        }
    } 
    
    @Override
    public void onListItemClick(ListView l, View v, int position, long id)
    {
        displayPost(v);
    }
    
    private void displayPost(View v)
    {
    	if (getListView().getPositionForView(v) != ListView.INVALID_POSITION)
    	{    		
    		this._postYLoc = v.getTop();
    		
    		expandAndCheckPost(getListView().getPositionForView(v));
    		
    		// calculate sizes
            Display display = ((WindowManager) v.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            DisplayMetrics displaymetrics = new DisplayMetrics();
            display.getMetrics(displaymetrics);
            
    		getListView().setSelectionFromTop(getListView().getPositionForView(v), _postYLoc - (_userNameHeight + (int)(30*(displaymetrics.ydpi / 160))));
    		final View view = v;
            final ListView listView = getListView();
            listView.post(new Runnable() {

                @Override
                public void run() {
                    listView.requestChildRectangleOnScreen(view,
                            new Rect(0, 0, view.getRight(), view.getHeight()), false);
                }
            });
    	}
    }
    
    private void expandAndCheckPost(int position)
    {
    	Post post = null;
    	// sanity check
    	if (position >= 0 && position < _adapter.getCount())
        {
    		post = _adapter.getItem(position);
        }
        
        // user clicked "Loading..."
        if (post == null)
            return;
        
        getListView().setItemChecked(position, true);
        
        // never unexpand the root post
        if ((_lastExpanded > 0) && (_lastExpanded <= _adapter.getCount()) && (_lastExpanded != position))
        {
        	Post oldpost = _adapter.getItem(this._lastExpanded);
        	oldpost.setExpanded(false);
        	getListView().setItemChecked(_lastExpanded, false);
        	
        	// scroll helper
        }
        
        _lastExpanded = position;
        _currentPostId = post.getPostId();
        _currentPostAuthor = post.getUserName();
        
        post.setExpanded(true);
       	
       	_adapter.notifyDataSetChanged();
    }
    
    class PostLoadingAdapter extends LoadingAdapter<Post>
    {
        boolean _lolsInPost = true;
        boolean _getLols = true;
        float _zoom = 1.0f;
        int _maxWidth = 400;
        int _bulletWidth = 20;
        int _maxBullets = 8;
        String _donatorList = "";
        boolean _showModTools = false;
        boolean _showPreviewUsername = true;
		private boolean _showHoursSince = true;
        private String _userName = "";
		private String _OPuserName = "";
        private HashMap<String, HashMap<String, LolObj>> _shackloldata = new HashMap<String, HashMap<String, LolObj>>();
        
        private Bitmap _bulletBlank;
        private Bitmap _bulletEnd;
        private Bitmap _bulletExtendPast;
        private Bitmap _bulletBranch;
        private Bitmap _bulletCollapse;
		private BitmapDrawable _donatorIcon;
		private boolean _displayLimes = true;
		private boolean _displayLolButton = false;
		private HashMap<String, LolObj> _threadloldata;
		private Bitmap _bulletEndNew;
		private Bitmap _bulletExtendPastNew;
		private Bitmap _bulletBranchNew;
		private LruCache<String, Bitmap> mMemoryCache;
		private boolean _fastScroll = true;
        
        public PostLoadingAdapter(Context context, ArrayList<Post> items)
        {
            super(context, R.layout.thread_row, items);
            loadPrefs();
            
            // Get max available VM memory, exceeding this amount will throw an
            // OutOfMemory exception. Stored in kilobytes as LruCache takes an
            // int in its constructor.
            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

            // Use 1/3th of the available memory for this memory cache.
            final int cacheSize = maxMemory / 3;

            mMemoryCache = new LruCache<String, Bitmap>(cacheSize)
            {
                @Override
                protected int sizeOf(String key, Bitmap bitmap)
                {
                    // The cache size will be measured in kilobytes rather than
                    // number of items.

                	if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
                        return ((bitmap.getRowBytes() * bitmap.getHeight()) / 1024) / 1024;
                    } else {
                    	return bitmap.getByteCount() / 1024;
                    }
                }
            };

        }
        public void addBitmapToMemoryCache(String key, Bitmap bitmap)
        {
            if ((bitmap != null) && (getBitmapFromMemCache(key) == null))
            {
                mMemoryCache.put(key, bitmap);
            }
        }

        public Bitmap getBitmapFromMemCache(String key) {
            return mMemoryCache.get(key);
        }
        public void clearBitmapMemCache()
        {
        	mMemoryCache.evictAll();
        }
        
        @Override
        public void clear()
        {
        	// reload preferences
            loadPrefs();
            
            clearBitmapMemCache();
            
            
            _lastExpanded = 0;
            
            // clear any errant checkeds
            for (int i=0; i < this.getCount(); i++) {
                getListView().setItemChecked(i, false);
            }
            
            super.clear();
        }
        
        void loadPrefs()
        {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ThreadViewFragment.this.getActivity());
            _userName = prefs.getString("userName", "").trim();
            _lolsInPost = prefs.getBoolean("showPostLolsThreadView", true);
            _getLols = prefs.getBoolean("getLols", true);
            _zoom = Float.parseFloat(prefs.getString("fontZoom", "1.0"));
            _showModTools = prefs.getBoolean("showModTools", false);
            _showPreviewUsername = prefs.getBoolean("showPreviewUsername", true);
            _showHoursSince  = prefs.getBoolean("showHoursSince", true);
            _fastScroll   = prefs.getBoolean("fastScroll", true);
            _donatorList = prefs.getString("limeUsers", "");
            _displayLimes  = prefs.getBoolean("displayLimes", true);
            _displayLolButton  = prefs.getBoolean("displayLolButton", false);
            
            // fast scroll on mega threads
        	if (getActivity () != null)
    		{
        		boolean set = false;
        		if ((getCount() > 300))
        			set = true;
        		final boolean set2 = set && _fastScroll;
        		
    			getActivity().runOnUiThread(new Runnable(){
	        		@Override public void run()
	        		{
	        			if (_viewAvailable)
	                	{
	        				if (getListView() != null)
	                    	{
	        					getListView().setFastScrollEnabled(set2);
	                    	}
	                	}
	        		}
    			});
    		}
            
            // calculate sizes for deep threads
            Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            DisplayMetrics displaymetrics = new DisplayMetrics();
            display.getMetrics(displaymetrics);
            _maxWidth = (int) (displaymetrics.widthPixels * (.7));
            createAllBullets();
        	_maxBullets = (int) Math.floor(_maxWidth / _bulletWidth);
        	// failsafe
        	if (_maxBullets < 8) _maxBullets = 8;
        }
        
        @Override
		protected void setCurrentlyLoading(boolean set)
        {
        	super.setCurrentlyLoading(set);
        	
    		if (getActivity () != null)
    		{
    			getActivity().runOnUiThread(new Runnable(){
	        		@Override public void run()
	        		{
	        			if (_viewAvailable)
	                	{
	        	        	updateThreadViewUi();
	                	}
	        		}
    			});
    		}
        }

        @SuppressLint("NewApi")
		@Override
        protected View createView(int position, View convertView, ViewGroup parent)
        {
        	// get the thread to display and populate all the data into the layout
            Post p = getItem(position);
            
            ViewHolder holder = (ViewHolder)convertView.getTag();
            if (holder == null)
            {
                holder = new ViewHolder();
                holder.container = (CheckableLinearLayout)convertView.findViewById(R.id.rowLayout);
                
                // preview items
                holder.previewView = (View)convertView.findViewById(R.id.previewView);
                holder.previewRow = holder.previewView;
                
				holder.treeIcon = (ImageView)convertView.findViewById(R.id.treeIcon); 
                
               
                holder.preview = (TextView)convertView.findViewById(R.id.textPreview);
                holder.previewLolCounts = (TextView)convertView.findViewById(R.id.textPostLolCounts);
                holder.previewUsername = (TextView)convertView.findViewById(R.id.textPreviewUserName);
                holder.previewLimeHolder = (ImageView)convertView.findViewById(R.id.previewLimeHolder);
                
                
                // zoom for preview.. needs to only be done ONCE, when holder is first created
                holder.preview.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.preview.getTextSize() * _zoom);
                holder.previewUsername.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.previewUsername.getTextSize() * _zoom);
                holder.previewLolCounts.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.previewLolCounts.getTextSize() * _zoom);
                
                // these expanded views are referenced in preview setup so much not be null
                holder.expandedView = (View)convertView.findViewById(R.id.expandedView);
                holder.expandedView2 = (View)convertView.findViewById(R.id.expandedView2);
                
                convertView.setTag(holder);
            }
            
            // this is done for optimization purposes. this way these findviewbyids are only run when user clicks a post vs scrolls over previews
            if (holder != null && (p.getExpanded() && holder.content == null))
            {
                // expanded items
                holder.content = (TextView)convertView.findViewById(R.id.textContent);
                holder.username = (TextView)convertView.findViewById(R.id.textUserName);
                holder.moderation = (TextView)convertView.findViewById(R.id.threadModeration);
                holder.postedtime = (TextView)convertView.findViewById(R.id.textPostedTime);
                holder.loading = (ProgressBar)convertView.findViewById(R.id.tview_loadSpinner);
                holder.treeIconExp = (ImageView)convertView.findViewById(R.id.treeIconExp);
                holder.expLolCounts = (TextView)convertView.findViewById(R.id.textExpPostLolCounts);
                holder.expandedLimeHolder = (ImageView)convertView.findViewById(R.id.expandedLimeHolder);
                holder.buttonOther = (ImageButton)convertView.findViewById(R.id.buttonPostOpt);
                holder.buttonReply = (Button)convertView.findViewById(R.id.buttonReplyPost);
                holder.buttonAllImages = (Button)convertView.findViewById(R.id.buttonOpenAllImages);
                holder.buttonLol = (Button)convertView.findViewById(R.id.buttonPostLOL);
                
                // zoom for expanded
                holder.content.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.content.getTextSize() * _zoom);
                holder.username.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.username.getTextSize() * _zoom);
                holder.postedtime.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.postedtime.getTextSize() * _zoom);
                holder.expLolCounts.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.expLolCounts.getTextSize() * _zoom);
                holder.buttonReply.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.buttonReply.getTextSize() * _zoom);
                holder.buttonAllImages.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.buttonAllImages.getTextSize() * _zoom);
                holder.buttonLol.setTextSize(TypedValue.COMPLEX_UNIT_PX, holder.buttonLol.getTextSize() * _zoom);
                
                if (_userNameHeight == 0)
                	_userNameHeight = (int) (holder.username.getTextSize() * _zoom);
                
                // buttons are already as small as they can be
                if (_zoom >= 0.9)
                {
                	ViewGroup.LayoutParams buttonLayout = holder.buttonOther.getLayoutParams();
	                buttonLayout.height = (int)Math.floor(buttonLayout.height * _zoom);
	                buttonLayout.width = (int)Math.floor(buttonLayout.width * _zoom);
	                holder.buttonOther.setLayoutParams(buttonLayout);
	                
	                buttonLayout = holder.buttonReply.getLayoutParams();
	                buttonLayout.height = (int)Math.floor(buttonLayout.height * _zoom);
	                buttonLayout.width = (int)Math.floor(buttonLayout.width * _zoom);
	                holder.buttonReply.setLayoutParams(buttonLayout);
	                
	                buttonLayout = holder.buttonAllImages.getLayoutParams();
	                buttonLayout.height = (int)Math.floor(buttonLayout.height * _zoom);
	                buttonLayout.width = (int)Math.floor(buttonLayout.width * _zoom);
	                holder.buttonAllImages.setLayoutParams(buttonLayout);
	                
	                buttonLayout = holder.buttonLol.getLayoutParams();
	                buttonLayout.height = (int)Math.floor(buttonLayout.height * _zoom);
	                buttonLayout.width = (int)Math.floor(buttonLayout.width * _zoom);
	                holder.buttonLol.setLayoutParams(buttonLayout);
                }
                convertView.setTag(holder);
            }
            
        	// set post bg color
        	// holder.previewRow.setBackgroundColor(getResources().getColor(R.color.post_background));
        	// holder.expandedView.setBackgroundColor(getResources().getColor(R.color.selected_post));
        	holder.previewRow.setBackgroundResource(getResources().getColor(R.color.transparent));
        	holder.container.setModTagsFalse();
        	
        	// ensure root post is expanded        	
            // load expanded data
            if (p.getExpanded())
            {
            	
            	// thread moderation ... holder.container view appearance for moderation is set elsewhere below
    			if (p.isNWS())
    			{
    				holder.moderation.setVisibility(View.VISIBLE);
    				holder.moderation.setTextColor(getResources().getColor(R.color.modtag_nws));
	                holder.moderation.setText("nws");
    			}
    			else if (p.isINF())
	            {
    				holder.moderation.setVisibility(View.VISIBLE);
	                holder.moderation.setTextColor(getResources().getColor(R.color.modtag_inf));
	                holder.moderation.setText("informative");
	            }
    			else if (p.isPolitical())
	            {
    				holder.moderation.setVisibility(View.VISIBLE);
	                holder.moderation.setTextColor(getResources().getColor(R.color.modtag_political));
	                holder.moderation.setText("political");
	            }
    			else
	            {
	            	holder.moderation.setVisibility(View.GONE);
	            }
    				
    				
            	// set content text color
                holder.content.setTextColor(getResources().getColor(R.color.nonpreview_post_text_color));
            	holder.content.setText(p.getFormattedContent());

            	// set lol tags
            	if (p.getLolObj() != null) { holder.expLolCounts.setText(p.getLolObj().getTagSpan()); }
            	else { holder.expLolCounts.setText(""); }
            	
            	// set username and color
            	holder.username.setText(p.getUserName());
            	if (p.getUserName().equalsIgnoreCase(_userName))
                {
                    // highlight your own posts
                    holder.username.setTextColor(getResources().getColor(R.color.selfUserName));
                }
        		else if (p.getUserName().equalsIgnoreCase(_OPuserName) && (position != 0))
        		{
        			holder.username.setTextColor(getResources().getColor(R.color.OPUserName));
        		}
        		else if (p.isFromEmployee())
        		{
        			holder.username.setTextColor(getResources().getColor(R.color.emplUserName));
        		}
        		else if (p.isFromModerator())
        		{
        			holder.username.setTextColor(getResources().getColor(R.color.modUserName));
        		}
        		else
        		{
        			holder.username.setTextColor(getResources().getColor(R.color.userName));
        		}
            	
            	// set posted time
            	// check if this post is so old its not even the same year
            	if (!TimeDisplay.getYear(TimeDisplay.now()).equals(TimeDisplay.getYear(p.getPosted())))
            		holder.postedtime.setText(TimeDisplay.convTime(p.getPosted(), "MMM dd, yyyy h:mma zzz"));
            	else
            	{
                    if ((!_showHoursSince) || (TimeDisplay.threadAge(p.getPosted()) > 24f))
                    {
                    	if (TimeDisplay.threadAge(p.getPosted()) > 96f)
                    		holder.postedtime.setText(TimeDisplay.convertTimeLong(p.getPosted()));
                    	else
                    		holder.postedtime.setText(TimeDisplay.convertTime(p.getPosted()));
                    }
                    else
                    	holder.postedtime.setText(TimeDisplay.threadAgeToString(p.getPosted()));
            	}
            	
            	// links stuff
            	holder.content.setLinkTextColor(getResources().getColor(R.color.linkColor));
            	holder.content.setMovementMethod(new CustomLinkMovementMethod());
            	// now we cant click the list item so fix that
            	 /* holder.content.setOnClickListener(new View.OnClickListener() {
            	    @Override
            	    public void onClick(View v) {
            	        // displayPost(v,false);
            	    }
            	}); */
            	
            	final int pos = position;
            	final String unamefinal = p.getUserName();
            	holder.username.setOnClickListener(new View.OnClickListener() {
            	    @Override
            	    public void onClick(View v) {
            	    	usernameActions(unamefinal);
            	    }
            	});
            	
            	holder.buttonReply.setOnClickListener(new View.OnClickListener() {
            	    @Override
            	    public void onClick(View v) {
            	    	postReply(_adapter.getItem(pos));
            	    }
            	});
            	
            	holder.buttonLol.setOnClickListener(new View.OnClickListener() {
            	    @Override
            	    public void onClick(View v) {
            	    	lolChoose(pos, true);
            	    }
            	});
            	
            	// open all images button
            	final CustomURLSpan[] urlSpans = ((SpannableString)holder.content.getText()).getSpans(0, holder.content.getText().length(), CustomURLSpan.class);
            	if (urlSpans.length > 1)
            	{
            		String _href;
        	    	ArrayList<String> hrefs = new ArrayList<String>();
        	    	for (int i = 0; i < urlSpans.length; i++)
        	    	{
        	    		_href = urlSpans[i].getURL().trim();
        	    		if (CustomURLSpan.isImage(_href))
        	    		{
        	    			hrefs.add(_href);
        	    		}
        	    	}
        	    	String[] hrefs2 = new String[hrefs.size()];
        	    	hrefs.toArray(hrefs2);
        	    	final String[] hrefs3 = hrefs2;
	            	holder.buttonAllImages.setOnClickListener(new View.OnClickListener() {
	            	    @Override
	            	    public void onClick(View v) {
	            	    	CustomURLSpan.openDialogBrowser(v.getContext(), hrefs3);
	            	    }
	            	});
	            	if (hrefs2.length > 1)
	            		holder.buttonAllImages.setVisibility(View.VISIBLE);
	            	else
	            		holder.buttonAllImages.setVisibility(View.GONE);
	            	
            	}
            	else
            	{
            		holder.buttonAllImages.setVisibility(View.GONE);
            	}
            	
            	// this must be done for recycled views
        		if (!_adapter.isCurrentlyLoading())
        		{
        			holder.container.setLoading(false);
        			holder.loading.setVisibility(View.GONE);
        		}
        		
            	// check if root post 
        		if (p.getPostId() == _rootPostId)
            	{    		
            		// and loading
            		if (_adapter.isCurrentlyLoading())
            		{
            			holder.expandedView2.setVisibility(View.VISIBLE);
            			holder.loading.setVisibility(View.VISIBLE);
            			holder.container.setLoading(true);
            		}
            		else
            		{
            			holder.expandedView2.setVisibility(View.GONE);
            			holder.loading.setVisibility(View.GONE);
            			holder.container.setLoading(false);
            		}
            	}
            	else
        		{
        			holder.expandedView2.setVisibility(View.GONE);
        			holder.loading.setVisibility(View.GONE);
        			holder.container.setLoading(false);
        		}
                
                
            	holder.buttonOther.setOnClickListener(new View.OnClickListener() {
            	    @Override
            	    public void onClick(View v) {
            	    	AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            	        builder.setTitle("Post Actions");
            	        final CharSequence[] itemsMsg = { "Shackmessage " + unamefinal, "Search for posts by " + unamefinal, "Copy Post Text"};
            	        final CharSequence[] itemsNoMod = { "Shackmessage " + unamefinal, "Search for posts by " + unamefinal, "Copy Post Text","Copy URL","Share Link to Post","LOLtag Post","See who LOLtagged" };
            	        final CharSequence[] itemsMod = { "Shackmessage " + unamefinal, "Search for posts by " + unamefinal, "Copy Post Text","Copy URL","Share Link to Post","LOLtag Post","See who LOLtagged", "Mod Tools"};
            	        CharSequence[] items = itemsNoMod;
            	        if ((_showModTools) && (_rootPostId != 0))
                        {
                        	items = itemsMod;
                        }
            	        if (_messageId > 0)
            	        {
            	        	items = itemsMsg;
            	        }
            	        builder.setItems(items, new DialogInterface.OnClickListener() {
            	            public void onClick(DialogInterface dialog, int item) {
            	            	if (item == 0)
            	                {
            	                	shackmessageTo(unamefinal);
            	                }
            	                else if (item == 1) 
            	                {
            	                	searchForPosts(unamefinal);
            	                }
            	                else if (item == 2)
            	                {
            	                	copyPostText(pos);
            	                }
            	                else if (item == 3) 
            	                {
            	                	copyURL(pos);
            	                }
            	                else if (item == 4) 
            	                {
            	                	shareURL(pos);
            	                }
            	                else if (item == 5) 
            	                {
            	                	lolChoose(pos, false);
            	                }
            	                else if (item == 6) 
            	                {
            	                	showTaggers(pos);
            	                }
            	                else if (item == 7)
            	                {
            	                	modChoose();
            	                }
            	                }});
            	        AlertDialog alert = builder.create();
            	        alert.setCanceledOnTouchOutside(true);
            	        alert.show();
            	    }
            	});
            	
            	// tree branch
            	buildTreeBranches(p, position, holder.treeIconExp);
            	
            	holder.expandedView.setVisibility(View.VISIBLE);
            	holder.expandedView2.setVisibility(View.VISIBLE);
            	holder.previewView.setVisibility(View.GONE);
            	
            	// donator icon
            	holder.expandedLimeHolder.setImageResource(android.R.color.transparent);
            	if (_displayLimes)
            	{
	            	if (_donatorList.contains(":" + p.getUserName() + ";"))
	        		{
	                	holder.expandedLimeHolder.setImageDrawable(_donatorIcon);
	        		}
            	}
            	
            	// donator lol button
            	if ((_displayLolButton) && (_messageId == 0))
            		holder.buttonLol.setVisibility(View.VISIBLE);
            	else
            		holder.buttonLol.setVisibility(View.GONE);
            }
            // preview view
            else
            {
            	holder.preview.setText(p.getPreview());
            	holder.preview.setLinkTextColor(getResources().getColor(R.color.linkColor));
            	if (_showPreviewUsername)
            	{
            		holder.previewUsername.setText(p.getUserName());
            		holder.previewUsername.setVisibility(View.VISIBLE);
            		if (p.getUserName().equalsIgnoreCase(_userName))
                    {
                        // highlight your own posts
                        holder.previewUsername.setTextColor(getResources().getColor(R.color.selfUserName));
                    }
            		else if (p.getUserName().equalsIgnoreCase(_OPuserName))
            		{
            			holder.previewUsername.setTextColor(getResources().getColor(R.color.OPUserName));
            		}
            		else if (p.isFromEmployee())
            		{
            			holder.previewUsername.setTextColor(getResources().getColor(R.color.emplUserName));
            		}
            		else if (p.isFromModerator())
            		{
            			holder.previewUsername.setTextColor(getResources().getColor(R.color.modUserName));
            		}
            		else
            		{
            			holder.previewUsername.setTextColor(getResources().getColor(R.color.userName));
            		}
            		
            		// donator icon
            		holder.previewLimeHolder.setImageResource(android.R.color.transparent);
                	if (_displayLimes)
                	{
    	            	if (_donatorList.contains(":" + p.getUserName() + ";"))
    	        		{
    	                	holder.previewLimeHolder.setImageDrawable(_donatorIcon);
    	        		}
                	}
                	
            	}
            	else holder.previewUsername.setVisibility(View.GONE);
            	if (p.getLolObj() != null) {
            		holder.previewLolCounts.setVisibility(View.VISIBLE);
            		holder.previewLolCounts.setText(p.getLolObj().getTagSpan());
            	}
            	else { holder.previewLolCounts.setVisibility(View.GONE); }
            	
            	
            	holder.previewView.setVisibility(View.VISIBLE);
            	holder.expandedView.setVisibility(View.GONE);
            	holder.expandedView2.setVisibility(View.GONE);
            	
            	// tree branch
            	buildTreeBranches(p, position, holder.treeIcon); 
            	
            }
            
            if (p.isNWS())
            {
            	holder.container.setNWS(true);
            	holder.previewRow.setBackgroundColor(getResources().getColor(R.color.tview_nws_postbg));
            }
            else if (p.isINF())
            {
            	holder.container.setInf(true);
            	holder.previewRow.setBackgroundColor(getResources().getColor(R.color.tview_inf_postbg));
            }
            else if (p.isPolitical())
            {
            	holder.container.setPolitical(true);
            }
            else
            {
            	holder.container.refreshDrawableState(); // needed because setmodtagsfalse does not do this
            }

            if ((!_showPreviewUsername) && p.getUserName().equalsIgnoreCase(_userName))
            {
                // highlight your own posts
                holder.preview.setTextColor(getResources().getColor(R.color.user_paricipated));
            }
            else
            {
                // highlight newer posts
            	
                int color = 255 - (12 * Math.min(p.getOrder(), 10));
                holder.preview.setTextColor(Color.argb(255, color, color, color)); 
            }
            
            return convertView;
        }
        
        private ImageView buildTreeBranches(Post t, int position, ImageView imageView)
        {
        	StringBuilder depthStr = new StringBuilder(t.getDepthString());
        	
        	
        	if (depthStr.length() > 0)
        	{
        		final String imageKey = Integer.toString(t.getPostId());

        	    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        	    if (bitmap == null) {

        	    	System.out.println("HAD TO REBUILD CACHE");
        	    	imageView.setImageBitmap(buildBranchesForPost(t));

        	    }
	        	else
	        	{
	        		imageView.setImageBitmap(bitmap);
	        	}
        	}
        	else
        	{
        		imageView.setImageResource(android.R.color.transparent);
        	}
        	imageView.setAdjustViewBounds(true);
        	return imageView;
        }
        
        
        private Bitmap buildBranchesForPost(Post t)
        {
        	int bulletWidth = _bulletBlank.getWidth();
        	StringBuilder depthStr = new StringBuilder(t.getDepthString());
        	
        	if (depthStr.length() > 0)
        	{
	        	Bitmap big = Bitmap.createBitmap(bulletWidth * depthStr.length(), _bulletBlank.getHeight(), Bitmap.Config.ARGB_4444);
	        	Canvas canvas = new Canvas(big);
	        	
	        	for (int i = 0; i < depthStr.length(); i++)
	        	{
	        		
	            	if (depthStr.charAt(i) == "L".charAt(0))
	            		canvas.drawBitmap(_bulletEnd, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "T".charAt(0))
	            		canvas.drawBitmap(_bulletBranch, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "|".charAt(0))
	            		canvas.drawBitmap(_bulletExtendPast, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "[".charAt(0))
	            		canvas.drawBitmap(_bulletEndNew, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "+".charAt(0))
	            		canvas.drawBitmap(_bulletBranchNew, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "!".charAt(0))
	            		canvas.drawBitmap(_bulletExtendPastNew, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "0".charAt(0))
	            		canvas.drawBitmap(_bulletBlank, bulletWidth * i, 0, null);
	            	if (depthStr.charAt(i) == "C".charAt(0))
	            		canvas.drawBitmap(_bulletCollapse, bulletWidth * i, 0, null);
	            	
	            	
	        	}
	        	addBitmapToMemoryCache(Integer.toString(t.getPostId()), big);
	        	return big;
        	}
        	return null;
        }
        
        private Bitmap createBullet(int id)
        {
        	Bitmap bm = BitmapFactory.decodeResource(getResources(), id);
        	if (_zoom != 1.0f)
        	{
	        	int newH = (int)(bm.getHeight() * _zoom);
	        	int newW = (int)(bm.getWidth() * _zoom);
	        	bm = Bitmap.createScaledBitmap(bm, newW, newH, false);
        	}
        	return bm;
        }
        
        private void createAllBullets()
        {
        	_bulletBlank = createBullet(R.drawable.bullet_blank);
        	_bulletEnd = createBullet(R.drawable.bullet_end);
        	_bulletExtendPast = createBullet(R.drawable.bullet_extendpast);
        	_bulletBranch = createBullet(R.drawable.bullet_branch);
        	_bulletEndNew = createBullet(R.drawable.bullet_endnew);
        	_bulletExtendPastNew = createBullet(R.drawable.bullet_extendpastnew);
        	_bulletBranchNew = createBullet(R.drawable.bullet_branchnew);
        	_bulletCollapse = createBullet(R.drawable.bullet_collapse);
        	_bulletWidth = _bulletCollapse.getWidth();
        	
        	// donator icon
        	int size =  (int) (_bulletCollapse.getHeight() * 0.75);
        	BitmapDrawable bm = new BitmapDrawable(getContext().getResources(), Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.icon), size, size, false));
        	ColorMatrix cm = new ColorMatrix();
        	cm.setSaturation(0.8f);
        	final float m[] = cm.getArray();
        	final float c = 0.8f;
        	final float bright = 0.9f;
        	cm.set(new float[] { 
        	        m[ 0] * c, m[ 1] * c, m[ 2] * c, m[ 3] * c, m[ 4] * c + bright, 
        	        m[ 5] * c, m[ 6] * c, m[ 7] * c, m[ 8] * c, m[ 9] * c + bright, 
        	        m[10] * c, m[11] * c, m[12] * c, m[13] * c, m[14] * c + bright, 
        	        m[15]    , m[16]    , m[17]    , m[18]    , m[19] }); 
            bm.setColorFilter(new ColorMatrixColorFilter(cm));
        	_donatorIcon = bm;
        }

        @Override
        protected ArrayList<Post> loadData() throws Exception
        {
        	if ((_rootPostId > 0) && (_messageId == 0))
        	{
	            ArrayList<Post> posts = ShackApi.processPosts(ShackApi.getPosts(_rootPostId, this.getContext()), _rootPostId, _maxBullets, getActivity());
	            _shackloldata = ShackApi.getLols(getActivity());
	            if (_shackloldata.size() != 0)
	            {
	            	// check if this thread has shacklol data
	            	if (_shackloldata.containsKey(Integer.toString(_rootPostId)))
	            	{
	            		_threadloldata = _shackloldata.get(Integer.toString(_rootPostId));
	            	}
	            }
	            
	            if (posts.size() > 0)
	            {
	            	// set op username for hilights
	            	_OPuserName = posts.get(0).getUserName();
	            }
	
	            if ((this.getCount() > 0) && (posts.size() > 0))
	            {
	            	// make sure the first items of both arent dupes from preloading the root post
	            	if (this.getItem(0).getPostId() == posts.get(0).getPostId())
	            	{
	            		posts.remove(0);
	            	}
	            }

	            // prebuild tree branches (optimization), do only first 100 to save from crashes
	            
	            for (int i = posts.size() -1; i >= 0; i--)
	            {
	            	Post p = posts.get(i);
	            	addBitmapToMemoryCache(Integer.toString(p.getPostId()), buildBranchesForPost(p));
	            }
	            for (Post p: posts)
	            {
	            	//

	            	// load lols
	            	if ((_getLols) && (_lolsInPost) && (_threadloldata != null) && (_threadloldata.containsKey(Integer.toString(p.getPostId()))))
	            	{
	            		p.setLolObj(_threadloldata.get(Integer.toString(p.getPostId())));
	            	}
	            }
	            
	            // fast scroll on mega threads
            	if (getActivity () != null)
        		{
            		boolean set = false;
            		if ((posts.size() > 300))
            			set = true;
            		final boolean set2 = set && _fastScroll;
            		
        			getActivity().runOnUiThread(new Runnable(){
    	        		@Override public void run()
    	        		{
    	        			if (_viewAvailable)
    	                	{
    	        				if (getListView() != null)
    	                    	{
    	        					getListView().setFastScrollEnabled(set2);
    	                    	}
    	                	}
    	        		}
        			});
        		}
        		
	            
	        
	            
	            return posts;
        	}
        	else
        		return null;
        }
        
        @Override
        protected void afterDisplay()
        {
        	Activity activ = getActivity();
        	if (activ != null)
        	{
	            // attempt at creating json so thread can be saved saveThread()
	            // this is for when we receive a loading command via intent without a thread json at the same time
	            boolean hasReplied = false;
	    		SharedPreferences _prefs = PreferenceManager.getDefaultSharedPreferences(activ);
	            String userName = _prefs.getString("userName", "");
	            
	            if (userName.length() > 0)
	            {
		    		for (int i = 0; i < _adapter.getCount(); i++)
		    		{
		    			if (_adapter.getItem(i).getUserName() == userName)
		    				hasReplied = true;
		    		}
	            }
	            
	            Thread t = null;
	            if ((_adapter != null) && (_adapter.getCount() > 0) && (_messageId == 0))
	            {
	            	// create fake thread for fav saving
	            	t = new Thread(_adapter.getItem(0).getPostId(), _adapter.getItem(0).getUserName(), _adapter.getItem(0).getContent(), _adapter.getItem(0).getPosted(), _adapter.getCount(), "ontopic", hasReplied, ((MainActivity)getActivity()).mOffline.containsThreadId(_rootPostId));
	            	if (_adapter.getItem(0) != null)
		    		{
		    			_rootPostId = _adapter.getItem(0).getPostId();
		    			expandAndCheckPost(0);
		    		}
	            }
	            
	            
	    		
	    		if (t != null)
	    		{
	    			// create data for fav saving
	    			if (t.getJson() != null)
	    				_lastThreadJson = t.getJson();
	    		}
	    		
	            try
	            {
	                ensurePostSelectedAndDisplayed();
	            } catch (Exception e)
	            {
	                Log.w("shackbrowse", "Error selecting currentid post", e);
	            }
	            
	            if (_autoFaveOnLoad)
	            {
	            	saveThread();
	            	_autoFaveOnLoad = false;
	            }
	            
	            // mark as read if in favs
	            if (((MainActivity)getActivity()).mOffline.containsThreadId(_rootPostId))
	            {
	            	((MainActivity)getActivity()).markFavoriteAsRead(_rootPostId, _adapter.getCount());
	            }
	            
	            updateThreadViewUi();
        	}
        }
        
        private class ViewHolder
        {
			public TextView moderation;
			public Button buttonLol;
			public ImageView expandedLimeHolder;
			public ImageView previewLimeHolder;
			public View anchor;
			public int currentPostId;
			public Button buttonAllImages;
			public View expandedView2;
			public View previewRow;
			public ProgressBar loading;
			public CheckableLinearLayout container;
			
			public ImageView treeIconExp; 
			ImageView treeIcon;
			
			/* OLD WAY TO DO BRANCHES
			LinearLayout treeIcon;
			public LinearLayout treeIconExp;
			*/
			public TextView previewUsername;
			public ImageButton buttonRefresh;
			public ImageButton buttonOther;
			public Button buttonReply;
			public TextView expLolCounts;
			public TextView previewLolCounts;
			public View expandedView;
			public View previewView;
			
            TextView content;
            TextView preview;
            TextView username;
            TextView postedtime;
        }
        
    }
    
   

    public void adjustSelected(int movement)
    {
    	if (_viewAvailable)
    	{
	        int index = _lastExpanded + movement;
	        if (index >= 0 && index < getListView().getCount())
	        {
	            expandAndCheckPost(index);
	            ensureVisible(index, 0);
	        }
    	}
    }
    
    void ensureVisible(int position, int minPos)
    {
    	ListView view = getListView();

        
        if (position < minPos || position >= view.getCount())
            return;
        
        int first = view.getFirstVisiblePosition();
        int last = view.getLastVisiblePosition();
        int destination = 0;
        
        if (position < first)
            destination = position;
        else if (position >= last)
            destination = (position - (last - first));
        
        if ((position < first) || (position >= last))
        {
        	view.setSelection(destination);
        }
        
        view.smoothScrollToPosition(position);
    }
}
